
#ifndef _TTG_PIPELINE_NODE_MANAGER_
#define _TTG_PIPELINE_NODE_MANAGER_
#include "Common/Definitions.h"

#include <vector>
#include "NodeWrapper.h"
#include "StandartNodes.h"

namespace ttg_internal
{
	namespace pipeline
	{
		//Contains all nodes of pipeline and manages its.
		template <class InType, class OutType,
				  class FirstNodeType, class LastNodeType>
		class NodeManager
		{
			private:
				std::vector<NavigatorNode *> nodes;
				FirstNodeType start;
				LastNodeType end;
			private:
				//Sets tag for all nodes, connected with first.
				static void setTagFor(NavigatorNode *first, int tag)
				{
					for (size_t i = 0; i < first->getOutputs().size(); i++)
						setTagFor(first->getOutputs()[i], tag);
					first->setTag(tag);
				}
				//Creates enumeration of all nodes, connected with "first".
				static std::vector<NavigatorNode *> enumerateNodes(NavigatorNode *first)
				{
					//Preparing.
					setTagFor(first, 0);
					std::vector<NavigatorNode *> res;
					std::vector<NavigatorNode *> stack;
					stack.reserve(128);
					stack.push_back(first);
					//Enumaerating.
					while (stack.size() != 0)
					{
						NavigatorNode *cur = stack[stack.size() - 1];
						stack.pop_back();
						if (cur->getTag() == 0)
						{
							res.push_back(cur);
							cur->setTag(1);
							for (size_t i = 0; i < cur->getOutputs().size(); i++)
								stack.push_back(cur->getOutputs()[i]);
						}
					}
					//Clearing and returning result.
					setTagFor(first, 0);
					return res;
				}
				//Makes names of nodes unique.
				static void makeUnique(std::vector<NavigatorNode *> &nodes)
				{
					ttg::structures::Dictionary<NavigatorNode> *unique
							= ttg::structures::Dictionary<NavigatorNode>::create(nodes.size());
					for (size_t i = 0; i < nodes.size(); i++)
					{
						const char *name = nodes[i]->getName();
						if (unique->get(name) != NULL)
						{
							int j = 1;
							char buf[256];
							do
							{
								sprintf_s(buf, 255, "%s_#%d", name, j++);
							}
							while (unique->get(buf) != NULL);
							nodes[i]->setName(buf);
							nodes[i]->getInfo().setName(buf);
						}
						unique->add(nodes[i]);
					}
					SAFE_DELETE(unique);
				}
			public:
				//Creates node manager for given node set.
				//Start and end of pipeline will be added automatically
				NodeManager(ttg::pipeline::NodeWrapper<InType, OutType> &wrapper)
				{
					//Concating with start of pipeline.
					for (size_t i = 0; i < wrapper.getInputs().size(); i++)
					{
						wrapper.getInputs()[i]->attachInput(&start);
						start.attachOutput(wrapper.getInputs()[i]);
					}
					//Concating with end of pipeline.
					for (size_t i = 0; i < wrapper.getOutputs().size(); i++)
					{
						wrapper.getOutputs()[i]->attachOutput(&end);
						end.attachInput(wrapper.getOutputs()[i]);
					}
					//Creating enumeration
					if (wrapper.getInputs().size() == 0 ||
						wrapper.getOutputs().size() == 0)
						throw ttg::Error(ttg::Error::FATAL, "NodeManager", "NodeManager",
										 "Cannot create pipeline for wrapper without input or output");
					nodes = enumerateNodes(&start);
					makeUnique(nodes);
				}
				inline void setTag(int tag)
				{ setTagFor(&start, tag); }
				//Returns enumeration with all nodes.
				inline std::vector<NavigatorNode *> &getNodes()
				{ return nodes; }
				//Returns start node.
				inline FirstNodeType &getPipelineStart()
				{ return start; }
				//Returns end node.
				inline LastNodeType &getPipelineEnd()
				{ return end; }
		};
	}
}

#endif
